package cn.org.farseer.sweetftp.core.persistent;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import cn.org.farseer.sweetftp.core.support.CustomService;
import cn.org.farseer.sweetftp.core.util.LoggerTools;

/**
 * 作者 : [Farseer.org] 简洋  —  2009-8-17
 * 
 * 项目 : [Farseer.org] SweetFTP
 *
 * 说明：IPersistent 持久化接口的xml实现类，该类会将对象及其属性保存到xml文件中，支持级联对象保存。
 */
@CustomService(IPersistent.class)
public class XmlPersistent implements IPersistent
{
	private static final Log logger = LogFactory.getLog(XmlPersistent.class);
	private PersistentConfigure configure;
	private static final String ATTRIBUTE_TYPE = "type";
	private static final String ATTRIBUTE_VALUE = "value";
	private static final String ELEMENT_ENTRY = "entry";
	private static final String ELEMENT_KEY = "key";
	private static final String ELEMENT_VALUE = "value";
	
	public XmlPersistent(){}
	
	public XmlPersistent (PersistentConfigure configure)
	{
		this.configure = configure;
	}
	
	public void setConfigure(PersistentConfigure configure)
	{
		this.configure = configure;
	}

	public PersistentConfigure getConfigure()
	{
		return configure;
	}

	/**
	 * 调用该方法持久化一组可以被持久化的对象。
	 * 需要传入一个IPersistentable接口参数以获取一些持久化所需信息。
	 * @param persistentables
	 * @return
	 */
	public boolean persistent(List<Object>  objects,IPersistentable persistentable)
	{
		if(objects == null || objects.size() == 0)
			return false;
		
		IXmlPersistentable xmlPersistentable = (IXmlPersistentable)persistentable;
		Document doc = DocumentHelper.createDocument();
		Element rootEle = doc.addElement(xmlPersistentable.getRootNodeName());
		
		for (Iterator it = objects.iterator();it.hasNext();)
		{
			Object oneObj = it.next();
			if(oneObj == null)
				continue;
			String eleName = oneObj.getClass().getSimpleName();
			try{
				persistentSubElement(oneObj,eleName, rootEle);
			}catch(Exception e)
			{
				LoggerTools.logError(logger,e);
				LoggerTools.logError(logger, "持久化集合元素时发生错误！元素名 : [" + eleName + "] ");
			}
		}
		
		outputXml(persistentable, doc);  //输出xml文档。
		
		return true;
	}
	
	
	/**
	 * 持久化单个对象。
	 * @param object
	 * @param persistentable
	 * @return
	 */
	public boolean persistentSingle(Object  object,IPersistentable persistentable)
	{
		IXmlPersistentable xmlPersistentable = (IXmlPersistentable)persistentable;
		Document doc = DocumentHelper.createDocument();
		Element rootEle = doc.addElement(xmlPersistentable.getRootNodeName());
		
		try{
			persistentSubElement(object,object.getClass().getSimpleName(), rootEle);
		}catch(Exception e)
		{
			LoggerTools.logError(logger,e);
			LoggerTools.logError(logger, "持久化单个元素时发生错误！元素: [" + rootEle + "] ");
		}
		
		outputXml(persistentable, doc);  //输出xml文档。
		
		return true;
	}

	/**
	 * 输出xml文档。
	 * @param persistentable
	 * @param doc
	 */
	private void outputXml(IPersistentable persistentable, Document doc)
	{
		XMLWriter output = null;
		try{
			OutputFormat format = OutputFormat.createPrettyPrint();
			output = new XMLWriter(new FileWriter(new File(configure.getPersistentBaseDir() + File.separator+  persistentable.getSaveName())),format);
			output.write(doc);
		}catch(Exception e)
		{
			LoggerTools.logError(logger,e);
			throw new PersistentException("持久化对象时，输出对象到Xml失败，错误信息： " + e,e);
		}finally{
			if(output != null)
			{
				try{ output.close(); }catch(Exception e1){}
			}
		}
	}
	
	/**
	 * 持久化xml的子元素。
	 * 分以下几种情况分别进行处理：
	 * 创建一个标签。
	 * 1. 如果是数组类型，则标签名需要获得其基本类型的名称，而不能使用数组类型名称。
	 * 2. 如果对象是集合类Collection的子类，type属性为该类的具体类型。接着迭代处理集合类的每一个元素。
	 * 3. 如果对象是Map的子类，则type属性为该类的具体类型，接着迭代该Map，每条记录创建一个子标签entry，在
	 *     entry标签下分别建立key标签和value标签，如果key、value是基本数据类型或者String类型，则在key，value标签中增加type属性和value属性。
	 *     如果不是，则递归。
	 * 4. 如果是基本数据类型或者字符串类型，则创建type属性和value属性，type属性表示该类型的名称，value表示该值转化为String后的值的。
	 * 5. 如果以上都不是，表示为复杂对象类型。type属性为类型名称，取出每一个属性，建立新标签，标签名为属性名，如果新属性为基本数据类型或字符串类型，
	 *     则type为类型名称，  value为具体属性的值，如果不是，则递归。
	 * 
	 * 注意，其他没考虑到的情况出异常的话需要扩展。
	 * @param oneObj
	 * @param eleName
	 * @param parent
	 * @throws Exception
	 */
	private void persistentSubElement(Object oneObj ,String eleName ,Element parent) throws Exception
	{
		if(oneObj == null)
			return;
		Element objEle = parent.addElement(eleName);
		if(oneObj.getClass().isArray())   //判断是否是数组。
		{
			Object[] arrayObject = (Object[])oneObj;
			objEle.addAttribute(ATTRIBUTE_TYPE, oneObj.getClass().getName());
			for (int i = 0; i < arrayObject.length; i++)
			{
				Object subObj = arrayObject[i];
				persistentSubElement(subObj ,getArrayBaseClass(subObj.getClass()).getSimpleName(),objEle);
			}
		}
		else if(oneObj instanceof Collection)
		{
			Collection collectionObj = (Collection)oneObj;
			objEle.addAttribute(ATTRIBUTE_TYPE, oneObj.getClass().getName());
			for (Iterator it = collectionObj.iterator();it.hasNext();)
			{
				Object subObj = it.next();
				persistentSubElement(subObj ,subObj.getClass().getSimpleName(),objEle);
			}
		}else if (oneObj instanceof Map)
		{
			Map mapObj = (Map)oneObj;
			objEle.addAttribute(ATTRIBUTE_TYPE, oneObj.getClass().getName());
			for (Iterator it = mapObj.keySet().iterator();it.hasNext();)
			{
				Element entryEle = objEle.addElement(ELEMENT_ENTRY);
				Element keyEle = entryEle.addElement(ELEMENT_KEY);
				Element valueEle = entryEle.addElement(ELEMENT_VALUE);
				Object key = it.next();
				Object value = mapObj.get(key);
				if(isBaseOrStringType(key))   //如果key是简单数据类型或者字符串类型，则将key值赋在<key>标签的属性value上。
				{
					keyEle.addAttribute(ATTRIBUTE_TYPE, key.getClass().getName());
					keyEle.addAttribute(ATTRIBUTE_VALUE, key.toString());
				}
				else   //如果是复杂类型，则递归。
					persistentSubElement(key,key.getClass().getSimpleName(),keyEle);
				
				if(isBaseOrStringType(value)) //如果value是简单数据类型或者字符串类型，则将value值赋在<value>标签的属性value上。
				{
					valueEle.addAttribute(ATTRIBUTE_TYPE, value.getClass().getName());
					valueEle.addAttribute(ATTRIBUTE_VALUE, value.toString());
				}
				else //如果是复杂类型，则递归。
					persistentSubElement(value,value.getClass().getSimpleName(),valueEle);
			}
		}else if(isBaseOrStringType(oneObj))
		{
			objEle.addAttribute(ATTRIBUTE_TYPE, oneObj.getClass().getName());
			objEle.addAttribute(ATTRIBUTE_VALUE, oneObj.toString());
		}else
		{
			objEle.addAttribute(ATTRIBUTE_TYPE, oneObj.getClass().getName());
			
			Map<String,Field> fields = this.buildSuperClassField(oneObj.getClass());
			for (Iterator<String> itFields = fields.keySet().iterator();itFields.hasNext();)
			{
				String key = itFields.next();
				Field valueField = fields.get(key);
				if(Modifier.isFinal(valueField.getModifiers()))
					continue;
				valueField.setAccessible(true);
				Object fieldValue = valueField.get(oneObj);
				if(fieldValue != null)
				{
					String subEleName = valueField.getName();
					persistentSubElement(fieldValue ,subEleName ,objEle);
				}
			}
		}
	}
	
	
	
	
	/**
	 * 从配置文件中读取出被持久化的对象。
	 * 需要传入一个IPersistentable接口参数以获取一些持久化所需信息。
	 * @param persistentable
	 * @return
	 */
	public List<Object> load(IPersistentable persistentable)
	{
		List result = new ArrayList();
		SAXReader reader = new SAXReader();
		try{
			File loadFile = new File(configure.getPersistentBaseDir() + File.separator + persistentable.getSaveName());
			if(!loadFile.exists())  //如果文件不存在，表明没有保存过，或者配置丢失。
				return result;
			
			Document doc = reader.read(loadFile);
			Element rootElement = doc.getRootElement();
			List<Element> eleObjs = rootElement.elements();
			for (Iterator<Element> itObj = eleObjs.iterator();itObj.hasNext();)
			{
				Element oneObj = itObj.next();
				result.add(parseElementObject(oneObj));
			}
			return result;
		}catch(Exception e)
		{
			LoggerTools.logError(logger,e);
			throw new PersistentException("加载集合持久化对象时发生错误！错误信息如下：" + e,e);
		}
	}
	
	/**
	 * 加载单个对象。
	 * @param persistentable
	 * @return
	 */
	public Object loadSingleObject(IPersistentable persistentable)
	{
		SAXReader reader = new SAXReader();
		try{
			Document doc = reader.read(new File(configure.getPersistentBaseDir() + File.separator + persistentable.getSaveName()));
			Element rootElement = doc.getRootElement();
			Element oneObj = (Element)rootElement.elements().get(0);
			return parseElementObject(oneObj);
		}catch(Exception e)
		{
			LoggerTools.logError(logger,e);
			throw new PersistentException("加载集合持久化对象时发生错误！错误信息如下：" + e,e);
		}
	}
	
	/**
	 * 解析xml的每一个元素。
	 * 先获取type属性，以判断类型。分以下几种情况处理：
	 * 1. 如果是数组类型，则需要使用Array类来实例化，直接Class.newInstance会失败。
	 * 2. 如果是基础类型或者字符串类型，则将value属性的值进行相应类型转换后返回。
	 * 3. 如果是复杂类型，继续细分为以下三种：
	 * 3.1 如果是集合类Collection类的子类，则实例化相应类型的集合类，并且迭代每一个自元素进行递归封装。
	 * 3.2 如果Map的子类，则实例化相应类型的Map,并且迭代每一个entry标签。取其下面的key标签和value标签，如果有type属性，
	 * 	  表示是基本类型，则将value属性赋予该值。如果是复杂类型，则递归处理。
	 * 3.3 如果是复杂对象类型，则循环每一个子标签，进行处理。
	 * 
	 * @param oneObj
	 * @return
	 * @throws Exception
	 */
	private Object parseElementObject(Element oneObj) throws Exception
	{
		Object newObj = null;
		String className = oneObj.attributeValue(ATTRIBUTE_TYPE);
		Class clazz = Class.forName(className);
		if(clazz.isArray())  //判断是否是数组类型。
		{
			List<Element> subElements = oneObj.elements();
			int arraySize = subElements.size();
			Object array = Array.newInstance(clazz.getComponentType(), arraySize);
			for (int i = 0; i < arraySize; i++)
			{
				Element element = (Element) subElements.get(i);
				Object subObj = parseElementObject(element);
				Array.set(array, i, subObj);
			}
			newObj = array;
		}else if(isBaseOrStringType(clazz))  //判断是否基础类型或者字符串类型。 
		{
			String value = oneObj.attributeValue(ATTRIBUTE_VALUE);
			if(value != null)
				newObj =  this.processValueToObject(clazz, value);
		}else   //如果复杂类型。
		{
			newObj = clazz.newInstance();
			if(newObj instanceof Collection)   //处理集合类型情况。
			{
				Collection collectionObj = (Collection)newObj;
				List<Element> subElements = oneObj.elements();
				for (Iterator<Element> it = subElements.iterator();it.hasNext();)
				{
					collectionObj.add(parseElementObject(it.next()));
				}
			}else if(newObj instanceof Map)   //处理Map类型情况。
			{
				Map mapObj = (Map)newObj;
				List<Element> subElements = oneObj.elements();
				for (Iterator<Element> it = subElements.iterator();it.hasNext();)
				{
					Element entryElement = it.next();   //迭代每一个entry。
					Element keyElement =(Element)entryElement.elements(ELEMENT_KEY).get(0);
					Element valueElement =(Element)entryElement.elements(ELEMENT_VALUE).get(0);
					Object keyObj,valueObj = null;
					if(keyElement.attribute(ATTRIBUTE_TYPE) == null)
						keyObj = parseElementObject((Element)keyElement.elements().get(0));
					else
						keyObj = parseElementObject(keyElement);
					
					if(valueElement.attribute(ATTRIBUTE_TYPE) == null)
						valueObj = parseElementObject((Element)valueElement.elements().get(0));
					else
						valueObj = parseElementObject(valueElement);
					mapObj.put(keyObj, valueObj);
				}
			}else    //处理复杂对象类型。
			{
				Map<String, Field> fields = this.buildSuperClassField(clazz);
				List<Element> subElements = oneObj.elements();
				for (Iterator<Element> it = subElements.iterator();it.hasNext();)
				{
					Element eleObj = it.next();    //迭代每一个子标签。
					String name = eleObj.getName();  //得到属性名。
					Field field = 	fields.get(name);     //得到代表该属性的对象。
					if(field == null)
						throw new RuntimeException("XML文件中存在属性： " + name + "  而所属对象却没有该属性，请检查对象情况。");
					field.setAccessible(true);    //设置属性对象可访问属性为true。
					String fieldType = eleObj.attribute(ATTRIBUTE_TYPE).getValue(); //得到属性type的值，该值为属性的类型。
					Attribute  valueAttribute = eleObj.attribute(ATTRIBUTE_VALUE);  //得到属性"value"对象，该对象的值表示属性实际的值。
					if(valueAttribute != null) 
					{
						String value = valueAttribute.getValue(); //得到属性value的值。
						if(value != null && !"".equals(value.trim()))
						{  
							//以下开始判断是否各种基本类型及字符串类型。
							Class tempClazz = Class.forName(fieldType);
							Object tempValue = this.processValueToObject(tempClazz, value);
							field.set(newObj, tempValue);
						}
					}else  //如果没有拿到属性"value"，则表示该标签所对应的类是一个复杂类型子属性，而不是一个基本类型。
					{
						Object fieldObj = parseElementObject(eleObj);
						field.set(newObj,fieldObj );   //递归处理自属性。
					}
				}
			}
		}
		return newObj;
	}
	
	
	
	/**
	 * 封装一个类中所有的属性为Map返回，包括父类的属性，只供本类调用。
	 * @param superClazz
	 * @return
	 */
	private Map<String,Field> buildSuperClassField(Class superClazz)
	{
		Map<String,Field> hashMap = new HashMap<String,Field>();
		if(!Object.class.getName().equals(superClazz.getName()))
		{
			Field fields[] = superClazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				hashMap.put(fields[i].getName(), fields[i]);
			}
			hashMap.putAll(this.buildSuperClassField(superClazz.getSuperclass()));
		}
		return hashMap;
	}
	
	/**
	 * 将一个字符传对象根据传入的类型转为相应类型。
	 * @param tempClazz 传入类型。
	 * @param value  字符串对象。
	 * @return
	 */
	private Object processValueToObject(Class tempClazz,String value)
	{
		if(tempClazz == Long.class)
		{
			return Long.parseLong(value);
		}else if(tempClazz == Integer.class)
		{
			return Integer.parseInt(value);
		}else if(tempClazz == Double.class)
		{
			return Double.parseDouble(value);
		}else if(tempClazz == Float.class)
		{
			return Float.parseFloat(value);
		}else if(tempClazz == Character.class)
		{
			return value.toCharArray()[0];
		}else if(tempClazz == Boolean.class)
		{
			return Boolean.parseBoolean(value);
		}else if(tempClazz == Byte.class)
		{
			return Byte.valueOf(value);
		}
		
		//如果等于字符串。
		return value;
	}
	
	/**
	 * 判断一个类型是否是基础数据类型或者字符串类型。
	 * @param clazz
	 * @return
	 */
	private boolean isBaseOrStringType(Class clazz)
	{
		if(clazz == String.class || clazz == Integer.class || clazz == Double.class || clazz == Long.class
				|| clazz == Float.class || clazz == Boolean.class || clazz == Byte.class || clazz == Character.class)
			return true;
		return false;
	}
	
	
	/**
	 * 判断一个对象是否是基础数据类型或者字符串类型。
	 * @param obj
	 * @return
	 */
	private boolean isBaseOrStringType(Object obj)
	{
		if(obj instanceof String     || obj instanceof Integer    || obj instanceof Double || obj instanceof Long
				|| obj instanceof Float      || obj instanceof Boolean   || obj instanceof Byte     || obj instanceof Character)
			return true;
		return false;
	}
	
	
	/**
	 * 得到一个数组类型的基础类型。
	 * @param arrayClazz
	 * @return
	 */
	private Class getArrayBaseClass(Class arrayClazz)
	{
		if(arrayClazz.getComponentType() == null)
			return arrayClazz;
		return arrayClazz.isArray() ?  getArrayBaseClass(arrayClazz.getComponentType()) : arrayClazz.getComponentType();
	}

}
